/*
 * Copyright 2009 Johnson Consulting Services
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * 
 */
package com.johnsoncs.gwt.widgets.server.rebind;

import java.io.PrintWriter;

import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.johnsoncs.gwt.widgets.client.rebind.ClassReference;

/**
 * @author wilj
 *
 */
public class ClassReferenceGenerator extends Generator {
	private static class GeneratorImpl {
		private final TreeLogger logger;
		private final GeneratorContext context;
		private final String typeName;
		private final TypeOracle oracle;
		private JClassType classType;
		private SourceWriter writer;

		private String generatedTypeName;

		public GeneratorImpl(final TreeLogger logger, final GeneratorContext context,
				final String typeName) {
			this.logger = logger;
			this.context = context;
			this.typeName = typeName;
			this.oracle = context.getTypeOracle();
		}

		public String generate() throws UnableToCompleteException {
			try {
				this.classType = oracle.getType(typeName);
				this.writer = getWriter();
				if (writer != null) {
					for (final JMethod method : classType.getMethods()) {
						final ClassReference ref = method.getAnnotation(ClassReference.class);
						if (ref != null) {
							generateMethod(method, ref);
						}
					}
					writer.commit(logger);
				}
			} catch (final Exception e) {
				logger.log(Type.ERROR, "Error generating " + typeName, e);
				throw new UnableToCompleteException();
			}
			return generatedTypeName;
		}

		/**
		 * @param method
		 * @param ref
		 */
		private void generateMethod(final JMethod method, final ClassReference ref) {
			writer.indent();
			writer.println("public String " + method.getName() + "() {");
			writer.indent();
			writer.println("return \"" + ref.value().getName() + "\";");
			writer.outdent();
			writer.println("}");
			writer.outdent();
		}

		public SourceWriter getWriter() {
			final String packageName = classType.getPackage().getName();
			final String simpleName = classType.getSimpleSourceName() + "GeneratedImpl";
			final ClassSourceFileComposerFactory composer =
					new ClassSourceFileComposerFactory(packageName, simpleName);

			generatedTypeName = packageName + "." + simpleName;

			if (classType.isClass() != null) {
				composer.setSuperclass(typeName);
			} else {
				composer.addImplementedInterface(typeName);
			}

			final PrintWriter printWriter = context.tryCreate(logger, packageName, simpleName);
			if (printWriter == null) {
				return null;
			} else {
				final SourceWriter sw = composer.createSourceWriter(context, printWriter);
				return sw;
			}
		}

	}

	/* (non-Javadoc)
	 * @see com.google.gwt.core.ext.Generator#generate(com.google.gwt.core.ext.TreeLogger, com.google.gwt.core.ext.GeneratorContext, java.lang.String)
	 */
	@Override
	public String generate(final TreeLogger logger, final GeneratorContext context, final String typeName)
			throws UnableToCompleteException {
		return new GeneratorImpl(logger, context, typeName).generate();
	}

}
